﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using Microsoft.Xna.Framework.Media;

namespace DARE
{
    /// <summary>
    /// A Vignette is a moveable item, used in games for Skills or Items in inventories or skills bars.
    /// These are items that can be droped on widgets with the IsContainer property set to true.
    /// This can be cumulated, so when a vignette is drop over another with the same ID, this first one is deleted and
    /// the count displayed on the second one is increased by the number of vignettes contained by the first one.
    /// For example in a RPG, you got a key in your inventory.
    /// When you drag the key and drop it on another key of the same type, you will have two keys on one single vignette.
    /// </summary>
    [AttrTypeDependentSerializable(typeof(CVignetteData))]
    public class CVignette : CButton
    {
        #region identifier

        /// <summary>
        /// this delegate will be invoked when the vignette will not be able to attach itself to a container.
        /// Prototype for this delegate:
        /// bool attachFailureEventHandler(CVignette sender, object data);
        /// </summary>
        /// <param name="sender">this will be the vignette that calls this delegate</param>
        /// <param name="data"></param>
        /// <returns>return true if you want the item to be destroyed</returns>
        public delegate bool attachFailureEventHandler(CVignette sender, object data);

        private enum ELocation
        {
            TOPLEFT = 0,
            TOPRIGHT,
            BOTTOMLEFT,
            BOTTOMRIGHT
        }


        #endregion

        #region fields

        private bool m_hasBeenMoved;
        private bool m_isProduceer; //Set this variable at true if you want that the vignette linked at the elems produces other vignettes.
        private bool m_forceMove = false;
        private CWidget m_lastAttachedTo = null;
        private CStaticText[] m_texts;

        /// <summary>
        /// This idCount is an id that auto increments itself when an instance of CVignette is created.
        /// If this instance was created by a Clone call, then the idCount will be decremented and the id of the base CVignette will be set to
        /// the new instance.
        /// </summary>
        private static int m_idCount = 0;
        private int m_id;
        private bool m_isCumulable = false;
        private int m_nbCumulated = 1;

        
        private int m_cumulLimit = 5;

        
        private long m_type;

        private Vector2 m_nbSize;

        private bool m_isDetached = false;

        #endregion

        #region properties

        /// <summary>
        /// This is the event raised whenever a vignette is dropped and doesn't know what to do.
        /// If the slots are full or if it is drop over nothing...
        /// </summary>
        public event attachFailureEventHandler AttachFailureEventHandler;

        public bool HasBeenMoved { get { return m_hasBeenMoved; } }

        /// <summary>
        /// If the vignette is produceer, it will not be moveable, but when we will try to move one, it will produce one new.
        /// This new one will be dragged by the cursor.
        /// It can be used for a skill in a skill book that you want to drag'n'drop in your shortcut bar.
        /// </summary>
        public bool IsProduceer { get { return m_isProduceer; } set { m_isProduceer = value; } }

        /// <summary>
        /// Gets/Sets the last widget to which this one was attached.
        /// </summary>
        public CWidget LastAttachedTo { get { return m_lastAttachedTo; } set { m_lastAttachedTo = value; } }

        public CStaticText TopLeftText { get { return m_texts[(int)ELocation.TOPLEFT]; } set { m_texts[(int)ELocation.TOPLEFT] = value; } }
        public CStaticText TopRightText { get { return m_texts[(int)ELocation.TOPRIGHT]; } set { m_texts[(int)ELocation.TOPRIGHT] = value; } }
        public CStaticText BottomLeftText { get { return m_texts[(int)ELocation.BOTTOMLEFT]; } set { m_texts[(int)ELocation.BOTTOMLEFT] = value; } }
        public CStaticText BottomRightText { get { return m_texts[(int)ELocation.BOTTOMRIGHT]; } set { m_texts[(int)ELocation.BOTTOMRIGHT] = value; } }

        /// <summary>
        /// Gets/Sets the type of the vignette corresponds to skill or item or other thing. It allow the inventory to reject an item that has not the good type.
        /// </summary>
        public long Type { get { return m_type; } set { m_type = value; } }

        /// <summary>
        /// Gets/Sets the identifier for the item/skill/thing represented by the vignette.
        /// </summary>
        public int ID { get { return m_id; } set { m_id = value; } }
        
        /// <summary>
        /// Allow the item to be cumulable, so that when a vignette of the same id is drop on this one, it will be cumulated.
        /// </summary>
        public bool IsCumulable { get { return m_isCumulable; } set { m_isCumulable = value; } }
        
        /// <summary>
        /// Defines the limit of vignettes that we can cumulate.
        /// </summary>
        public int CumulLimit { get { return m_cumulLimit; } set { m_cumulLimit = value; } }
        
        /// <summary>
        /// Gets/Sets the number of cumulated vignettes
        /// </summary>
        public int Cumulated { get { return m_nbCumulated; } set { m_nbCumulated = value; } }

        #endregion

        #region ctor

        public CVignette(CHUDSystem hud, CWidget parent, string name)
            : base(hud, parent, name)
        {
            m_idCount++;
            m_type = 0;
            m_id = m_idCount;
            m_hasBeenMoved = false;
            IsAlwaysOnTop = true;
            CStaticText st1 = HUD.GetElement(name + "TOPLEFT") as CStaticText;
            CStaticText st2 = HUD.GetElement(name + "TOPRIGHT") as CStaticText;
            CStaticText st3 = HUD.GetElement(name + "BOTTOMLEFT") as CStaticText;
            CStaticText st4 = HUD.GetElement(name + "BOTTOMRIGHT") as CStaticText;
            m_texts = new CStaticText[4]
            {
                st1 == null ? new CStaticText(HUD, this, name + "TOPLEFT") : st1,
                st2 == null ? new CStaticText(HUD, this, name + "TOPRIGHT") : st2,
                st3 == null ? new CStaticText(HUD, this, name + "BOTTOMLEFT") : st3,
                st4 == null ? new CStaticText(HUD, this, name + "BOTTOMRIGHT") : st4
            };
            m_texts[(int)ELocation.TOPLEFT].Position = new Vector2(.05f, .05f);
            m_texts[(int)ELocation.TOPRIGHT].Position = new Vector2(.5f, .05f);
            m_texts[(int)ELocation.BOTTOMLEFT].Position = new Vector2(.05f, .5f);
            m_texts[(int)ELocation.BOTTOMRIGHT].Position = new Vector2(.7f, .5f);

            m_texts[(int)ELocation.TOPLEFT].Color = Color.White;
            m_texts[(int)ELocation.TOPRIGHT].Color = Color.White;
            m_texts[(int)ELocation.BOTTOMLEFT].Color = Color.White;
            m_texts[(int)ELocation.BOTTOMRIGHT].Color = Color.White;

            //m_texts[(int)ELocation.BOTTOMRIGHT].Texture = HUD.Skinner.GetTexture("defaultbackground");

            m_texts[(int)ELocation.TOPLEFT].Actualize();
            m_texts[(int)ELocation.TOPRIGHT].Actualize();
            m_texts[(int)ELocation.BOTTOMLEFT].Actualize();
            m_texts[(int)ELocation.BOTTOMRIGHT].Actualize();
            m_nbSize = m_texts[0].Font.MeasureString("7");

            m_nbCumulated = 1;
            Size = new Vector2(32, 32);
        }

        #endregion

        #region clone

        /// <summary>
        /// When you clone a CVignette, the id of the cloned vignette will be the same as the returning Vignette.
        /// </summary>
        /// <param name="w"></param>
        /// <returns></returns>
        protected override CWidget Clone(ref CWidget w)
        {
            ++m_nbClones;
            (w as CVignette).m_id = m_id;
            --m_idCount;
            (w as CVignette).m_type = m_type;
            (w as CVignette).m_isCumulable = m_isCumulable;
            (w as CVignette).AttachFailureEventHandler = AttachFailureEventHandler;
            return base.Clone(ref w);
        }


        #endregion

        #region show / hide

        public override void Hide()
        {
            m_isVisible = false;
        }

        public override void Show()
        {
            /*CWidget over = null;
            if (HUD != null)
                over = HUD.GetElement(m_rect, this);
            bool toShow = true;
            if (over != null)
            {
                if (over.m_isContainer)
                {
                    if (HUD.Widgets.ContainsValue(this))
                        HUD.Remove(this.m_name);
                    this.m_parent = over;
                    this.m_parent.Children.Add(this.m_name, this);
                    this.m_parent.giveSubRect(ref m_rect);
                }
                else
                    toShow = false;
            }
            if (toShow)*/
            base.Show();
            //            base.Show();
        }

        #endregion

        #region utils

        public override bool Attach(CWidget from)
        {
            return Attach(from, null);
        }

        /// <summary>
        /// This attachs a containable widget to a container in a widget that is a container group (`IsContainerGroup = true`)
        /// </summary>
        /// <param name="from">This is the widget to be attached</param>
        /// <param name="to">Can be a cumulable widget to which we want to attach the "from" widget. For example, if you drop a Vignette on another representing the same item, and if this vignette is cumulable, set the give the vignette on which you will attach the first one.</param>
        public override bool Attach(CWidget from, CWidget to)
        {
            if (m_parent != null && m_parent.IsContainer && m_parent.Parent != null && m_parent.Parent.IsContainerGroup)
                return m_parent.Parent.Attach(from);
            return false;
        }

        public override CWidget GetElement(CMouse mouse)
        {
            return GetElement(mouse, (CWidget)null);
        }
        /// <summary>
        /// [Deprecated]
        /// </summary>
        /// <returns>this</returns>
        public override CWidget GetElement(CMouse mouse, CWidget except)
        {
            return this;
        }

        /// <summary>
        /// [Deprecated]
        /// </summary>
        /// <returns>this</returns>
        public override CWidget GetElement(CMouse mouse, Type except)
        {
            return this;
        }

        public override CWidget GetElement(Rectangle pos)
        {
            return GetElement(pos, (CWidget)null);
        }
        /// <summary>
        /// [Deprecated]
        /// </summary>
        /// <returns>this</returns>
        public override CWidget GetElement(Rectangle pos, CWidget except)
        {
            return this;
        }

        /// <summary>
        /// [Deprecated]
        /// </summary>
        /// <returns>this</returns>
        public override CWidget GetElement(Rectangle pos, Type except)
        {
            return this;
        }

        #endregion

        #region events

        /// <summary>
        /// Actualizes the widget.
        /// If anything has changed, it will allow this widget to get back its parameters.
        /// For example, if the size of the widget is a percentage of the size of its parent,
        /// if the parent's size changes, it will recalculate the good size of this widget.
        /// (This method is called automatically but you can force a new actualization).
        /// </summary>
        public override void Actualize()
        {
            if (m_parent != null)
                this.m_parent.giveSubRect(ref m_rect);
            m_texture.Rectangle = m_rect;
            foreach (CWidget child in m_children.Values)
                child.Actualize();
        }

        /// <summary>
        /// Method called whenever the mouse is pressed and is over this widget
        /// </summary>
        public override bool onClick(CMouse mouse)
        {
            if ((IsLeftClicked && Mouse.GetState().RightButton == ButtonState.Pressed) || (IsRightClicked && Mouse.GetState().LeftButton == ButtonState.Pressed))
                return true;
            bool ret = base.onClick(mouse);
            if (IsLeftClicked || IsRightClicked)
                m_isMoving = true;
            return ret;
        }

        private bool DropVignette(CMouse mouse)
        {
            bool isAttached = false;
            CWidget over = HUD.GetElement(mouse, this.GetType());
            if (over != null)
            {
                if (over.IsContainer && over.Children.Count == 0 && over.Parent != null && (over.Parent as IHudContainerGroup).IsTypeAllowed(m_type))
                {
                    if (HUD.Widgets.ContainsValue(this))
                        HUD.Remove(this.m_name);
                    if (m_parent != null)
                        this.m_parent.Children.Remove(m_name);
                    this.m_parent = over;
                    if (this.Parent.Children.ContainsKey(this.Name))
                        this.Parent.Children.Remove(this.Name);
                    this.m_parent.Children.Add(this.m_name, this);
                    this.Actualize();
                    isAttached = true;
                }
                else if (over.IsContainer && over.Parent != null && (over.Parent as IHudContainerGroup) != null && (over.Parent as IHudContainerGroup).IsTypeAllowed(m_type))
                    isAttached = over.Parent.Attach(this, over.Children.First().Value);
                else if (over.IsContainerGroup && (over as IHudContainerGroup).IsTypeAllowed(m_type))
                    isAttached = over.Attach(this);
                else
                {
                    bool destroy = false;
                    if (AttachFailureEventHandler != null)
                        destroy = AttachFailureEventHandler.Invoke(this, null);
                    if (destroy)
                    {
                        if (!HUD.Widgets.ContainsValue(this))
                            HUD.Add(this);
                        this.Hide();
                    }
                    isAttached = destroy;
                }
                if (!isAttached)
                {
                    if (HUD.Widgets.ContainsValue(this))
                        HUD.Remove(this.m_name);
                    if (m_parent != null)
                        this.m_parent.Children.Remove(m_name);
                    this.m_parent = m_lastAttachedTo;
                    if (m_parent != null)
                    {
                        if (this.Parent.Children.Count > 0)
                        {
                            m_parent = null;
                            return true;
                        }
                        this.m_parent.Children.Add(this.m_name, this);
                        this.Actualize();
                    }
                }
            }
            else if (DestroyVignette())
                return false;
            m_lastAttachedTo = m_parent;
            return true;
        }

        /// <summary>
        /// Method called whenever the mouse is released and is over this widget and was pressed over this widget
        /// </summary>
        public override bool onRelease(CMouse mouse)
        {
            if (!IsLeftClicked && !IsRightClicked && !IsMiddleClicked)
                return false;
            if ((IsLeftClicked && Mouse.GetState().LeftButton == ButtonState.Pressed) ||
                (IsRightClicked && Mouse.GetState().RightButton == ButtonState.Pressed) ||
                (IsMiddleClicked && Mouse.GetState().MiddleButton == ButtonState.Pressed))
                return false;
            if (IsLeftClicked || m_isDetached)
            {
                m_isMoving = false;
                IsLeftClicked = false;
                if (m_releasedTexture != null)
                    this.m_texture = m_releasedTexture;
                if (m_hasBeenMoved)
                {
                    bool ret = DropVignette(mouse);
                    if (!ret)
                        return false;
                }
                else
                {
                    if (m_activatedCWidget != null)
                        m_activatedCWidget.onRelease(mouse);
                    InvokeRelease(this);
                }
                m_hasBeenMoved = false;
            }
            if (IsRightClicked)
            {
                IsRightClicked = false;
                InvokeRelease(this);
            }
            m_isDetached = false;
            return true;
        }

        #endregion

        /// <summary>
        /// Checks if the mouse is over this widget
        /// </summary>
        public override bool isMouseOver(CMouse mouse)
        {
            return base.isMouseOver(mouse);
        }

        #region actions on vignettes

        public void Detach()
        {
            if (m_parent != null)
                this.m_parent.Children.Remove(m_name);
            if (!HUD.Widgets.ContainsValue(this))
                HUD.Add(this, true);
            this.Hide();
        }

        private bool DestroyVignette()
        {
            bool destroy = false;
            if (AttachFailureEventHandler != null)
                destroy = AttachFailureEventHandler.Invoke(this, null);
            if (!destroy)
            {
                if (HUD.Widgets.ContainsValue(this))
                    HUD.Remove(this.m_name);
                if (m_parent != null)
                    this.m_parent.Children.Remove(m_name);
                this.m_parent = m_lastAttachedTo;
                if (m_parent != null)
                {
                    if (m_parent.Parent.IsContainerGroup)
                        if (!m_parent.Parent.Attach(this) && m_lastAttachedTo != null)
                        {
                            if (this.Parent.Children.ContainsKey(this.Name))
                                this.Parent.Children.Remove(this.Name);
                            this.m_parent.Children.Add(this.m_name, this);
                            this.Actualize();
                        }
                }
                else
                {
                    if (!HUD.Widgets.ContainsValue(this))
                        HUD.Add(this);
                    this.Hide();
                }
                m_lastAttachedTo = m_parent;
                m_hasBeenMoved = false;
                return true;
            }
            if (!HUD.Widgets.ContainsValue(this))
                HUD.Add(this);
            this.Hide();
            return false;
        }

        private void ProduceVignette()
        {
            m_hasBeenMoved = false;
            m_isMoving = false;
            IsLeftClicked = false;
            if (m_releasedTexture != null && m_texture != m_releasedTexture)
                this.m_texture = m_releasedTexture;
            CMouse.EButton button = CMouse.EButton.LEFT;
            CWidget vig = this.Clone();
            HUD.Add(vig);
            MouseState ms = Mouse.GetState();
            if (ms.LeftButton == ButtonState.Pressed)
            {
                button = CMouse.EButton.LEFT;
                vig.IsLeftClicked = true;
            }
            else if (ms.RightButton == ButtonState.Pressed)
            {
                button = CMouse.EButton.RIGHT;
                vig.IsRightClicked = true;
            }
            else if (ms.MiddleButton == ButtonState.Pressed)
            {
                button = CMouse.EButton.MIDDLE;
                vig.IsMiddleClicked = true;
            }
            CDare.Instance.InputMgr.Mouse.ButtonPressedEvent.Add(button);
            (vig as CVignette).IsProduceer = false;
            (vig as CVignette).m_forceMove = true;
            (vig as CVignette).m_hasBeenMoved = true;
            vig.Show();
            vig.onClick(CDare.Instance.InputMgr.Mouse);
            HUD.WidgetClicked = vig;
        }

        private void DetachVignette()
        {
            ProduceVignette();
            (HUD.WidgetClicked as CVignette).m_isDetached = true;
            (HUD.WidgetClicked as CVignette).m_isMoving = true;
            m_nbCumulated--;
            BottomRightText.Text = m_nbCumulated.ToString();
            (HUD.WidgetClicked as CVignette).m_lastAttachedTo = m_parent;
        }

        private void MoveVignette()
        {
            if (!m_hasBeenMoved && this.m_parent != null)
            {
                m_lastAttachedTo = m_parent;
                CWidget p = this.m_parent;
                this.m_parent = null;
                p.Children.Remove(this.m_name);
                if (HUD.Widgets.ContainsKey(m_name))
                    HUD.Remove(m_name);
                HUD.Add(this);
            }
            HUD.WidgetClicked = this;
            m_hasBeenMoved = true;
            this.m_rect.X = (int)(CDare.Instance.InputMgr.Mouse.Position.X - (m_rect.Width / 2.0f));
            this.m_rect.Y = (int)(CDare.Instance.InputMgr.Mouse.Position.Y - (m_rect.Height / 2.0f));
            this.Texture.Rectangle = m_rect;
            if (m_releasedTexture != null)
                m_releasedTexture.Rectangle = m_rect;
            if (m_clickedTexture != null)
                m_clickedTexture.Rectangle = m_rect;
        }

        #endregion

        #region update

        public override bool Update(GameTime gameTime)
        {
            if (this.m_nbCumulated <= 1)
                BottomRightText.Hide();
            else
            {
                BottomRightText.Show();
            }
            if (!IsLeftClicked && !IsRightClicked && !IsMiddleClicked)
            {
                BottomRightText.Position = new Vector2(m_rect.Width - (m_nbSize.X * BottomRightText.Text.Length), m_rect.Height - m_nbSize.Y);
                foreach (CWidget child in Children.Values)
                    child.Actualize();
                return base.Update(gameTime);
            }
            if ((m_isMoving && CDare.Instance.InputMgr.Mouse.HasMoved) || m_forceMove)
            {
                if (IsLeftClicked || m_isDetached)
                {
                    if (m_isProduceer)
                        ProduceVignette();
                    else
                        MoveVignette();
                }
                else if (!m_isProduceer && IsRightClicked)
                {
                    if (m_nbCumulated > 1)
                        DetachVignette();
                }
            }
            m_forceMove = false;
            foreach (CWidget child in Children.Values)
                child.Actualize();
            return base.Update(gameTime);
        }

                public override bool Draw(GameTime gameTime)
                {
                    base.Draw(gameTime);
                    
                    return true;
                }

        #endregion
    }
}
